#!/usr/bin/env python
# -*- coding: utf-8 -*-

from typing import List, Dict, Any, Optional, Tuple
from datetime import datetime

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy import update, delete

from app.core.exceptions import DatabaseError, NotFoundError
from app.core.logging import get_logger, LoggerMixin
from app.core.utils import generate_uuid
from app.models.database import Project

# 创建日志记录器
logger = get_logger("project_repo")


class ProjectRepository(LoggerMixin):
    """项目仓库
    
    管理项目数据
    """
    
    def __init__(self, session: AsyncSession):
        """初始化项目仓库
        
        Args:
            session: 数据库会话
        """
        self.session = session
    
    async def create(self, name: str, description: Optional[str] = None) -> Dict[str, Any]:
        """创建项目
        
        Args:
            name: 项目名称
            description: 项目描述
            
        Returns:
            Dict[str, Any]: 创建的项目数据
            
        Raises:
            DatabaseError: 当创建失败时
        """
        try:
            # 生成项目ID
            project_id = generate_uuid()
            
            # 创建项目
            project = Project(
                id=project_id,
                name=name,
                description=description
            )
            
            # 添加到会话
            self.session.add(project)
            await self.session.flush()
            
            # 创建项目字典
            project_dict = {
                "id": project_id,
                "name": name,
                "description": description,
                "created_at": datetime.now().isoformat(),
                "updated_at": datetime.now().isoformat()
            }
            
            self.logger.info(f"已创建项目: {project_id}")
            return project_dict
            
        except IntegrityError as e:
            await self.session.rollback()
            error_msg = f"创建项目失败，可能存在重复: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)
        except Exception as e:
            await self.session.rollback()
            error_msg = f"创建项目失败: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)
    
    async def get_by_id(self, project_id: str) -> Dict[str, Any]:
        """通过ID获取项目
        
        Args:
            project_id: 项目ID
            
        Returns:
            Dict[str, Any]: 项目数据
            
        Raises:
            NotFoundError: 当项目不存在时
        """
        try:
            # 查询项目
            result = await self.session.execute(
                select(Project).filter(Project.id == project_id)
            )
            project = result.scalars().first()
            
            # 检查项目是否存在
            if not project:
                raise NotFoundError(
                    message=f"项目不存在: {project_id}",
                    resource_type="project",
                    resource_id=project_id
                )
            
            # 转换为字典
            return {
                "id": project.id,
                "name": project.name,
                "description": project.description,
                "created_at": project.created_at.isoformat() if project.created_at else None,
                "updated_at": project.updated_at.isoformat() if project.updated_at else None
            }
            
        except NotFoundError:
            raise
        except Exception as e:
            error_msg = f"获取项目失败: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)
    
    async def update(
        self,
        project_id: str,
        name: Optional[str] = None,
        description: Optional[str] = None
    ) -> Dict[str, Any]:
        """更新项目
        
        Args:
            project_id: 项目ID
            name: 项目名称
            description: 项目描述
            
        Returns:
            Dict[str, Any]: 更新后的项目数据
            
        Raises:
            NotFoundError: 当项目不存在时
            DatabaseError: 当更新失败时
        """
        try:
            # 检查项目是否存在
            project_dict = await self.get_by_id(project_id)
            
            # 准备更新数据
            update_data = {}
            if name is not None:
                update_data["name"] = name
            if description is not None:
                update_data["description"] = description
            
            # 如果没有要更新的数据，直接返回
            if not update_data:
                return project_dict
            
            # 更新项目
            await self.session.execute(
                update(Project)
                .where(Project.id == project_id)
                .values(**update_data)
            )
            await self.session.flush()
            
            # 重新获取项目
            result = await self.session.execute(
                select(Project).filter(Project.id == project_id)
            )
            updated_project = result.scalars().first()
            
            # 转换为字典
            updated_dict = {
                "id": updated_project.id,
                "name": updated_project.name,
                "description": updated_project.description,
                "created_at": updated_project.created_at.isoformat() if updated_project.created_at else None,
                "updated_at": updated_project.updated_at.isoformat() if updated_project.updated_at else None
            }
            
            self.logger.info(f"已更新项目: {project_id}")
            return updated_dict
            
        except NotFoundError:
            raise
        except Exception as e:
            await self.session.rollback()
            error_msg = f"更新项目失败: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)
    
    async def delete(self, project_id: str) -> None:
        """删除项目
        
        Args:
            project_id: 项目ID
            
        Raises:
            NotFoundError: 当项目不存在时
            DatabaseError: 当删除失败时
        """
        try:
            # 检查项目是否存在
            await self.get_by_id(project_id)
            
            # 删除项目
            await self.session.execute(
                delete(Project).where(Project.id == project_id)
            )
            await self.session.flush()
            
            self.logger.info(f"已删除项目: {project_id}")
            
        except NotFoundError:
            raise
        except Exception as e:
            await self.session.rollback()
            error_msg = f"删除项目失败: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)
    
    async def list_all(
        self,
        limit: int = 10,
        offset: int = 0
    ) -> Tuple[List[Dict[str, Any]], int]:
        """列出所有项目
        
        Args:
            limit: 限制数量
            offset: 偏移量
            
        Returns:
            Tuple[List[Dict[str, Any]], int]: 项目列表和总数
            
        Raises:
            DatabaseError: 当查询失败时
        """
        try:
            # 查询项目
            result = await self.session.execute(
                select(Project)
                .order_by(Project.created_at.desc())
                .limit(limit)
                .offset(offset)
            )
            projects = result.scalars().all()
            
            # 查询总数
            count_result = await self.session.execute(
                select(Project.id)
            )
            total = len(count_result.scalars().all())
            
            # 转换为字典列表
            project_dicts = []
            for project in projects:
                project_dicts.append({
                    "id": project.id,
                    "name": project.name,
                    "description": project.description,
                    "created_at": project.created_at.isoformat() if project.created_at else None,
                    "updated_at": project.updated_at.isoformat() if project.updated_at else None
                })
            
            return project_dicts, total
            
        except Exception as e:
            error_msg = f"列出项目失败: {str(e)}"
            self.logger.error(error_msg)
            raise DatabaseError(error_msg)